package com.wty.shop.task;

import com.wty.shop.common.Const;
import com.wty.shop.common.RedissonManager;
import com.wty.shop.service.IOrderService;
import com.wty.shop.util.PropertiesUtil;
import com.wty.shop.util.RedisShardedPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

/**
 * @Author wangtengyu
 * @Create 2018-02-05-9:44
 */
@Component
@Slf4j
public class CloseOrderTask {

    @Autowired
    private IOrderService iOrderService;

    @Autowired
    private RedissonManager redissonManager;

    @PreDestroy
    public void delLock() {
        RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
    }

    //@Scheduled(cron = "0 */1 * * * *")
    public void closeOrderTaskV1() {
        log.info("关闭订单定时任务启动");
        int hour = Integer.parseInt(PropertiesUtil.getProperty("closeOrder.task.time.hour", "2"));
        //iOrderService.closeOrder(hour);
        log.info("关闭订单定时任务结束");
    }


    public void closeOrderTaskV2() {
        log.info("关闭订单定时任务启动");
        //定义过期时间 定义为long为为了与当前时间做加减
        long lockTimeOut = Long.parseLong(PropertiesUtil.getProperty("lock.timeout", "5000"));

        Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
        if (setnxResult != null && setnxResult.intValue() == 1) {
            //说明设置成功,获取锁成功，执行逻辑
            closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        } else {
            log.info("没有获得分布式锁 {}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        }

        log.info("关闭订单定时任务结束");
    }


    //@Scheduled(cron = "0 */1 * * * *")
    public void closeOrderTaskV3() {
        log.info("关闭订单定时任务启动");
        //定义过期时间 定义为long为为了与当前时间做加减
        long lockTimeOut = Long.parseLong(PropertiesUtil.getProperty("lock.timeout", "5000"));
        Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
        if (setnxResult != null && setnxResult.intValue() == 1) {
            //说明设置成功,获取锁
            closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        } else {
            //未获取到锁，继续判断，判断时间戳，看是否能重置锁
            //注意：未获取到锁的时候，多个线程都在执行这段代码.所以要有个是否安全的判断

            String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            //判断锁是否过期

            //lockValueStr!=null 为什么有这个判断，因为是集群多个进程在执行这段代码，如果lockValueStr==null说明发生了改变，说明别的进程调用了closeOrder逻辑，获得到了锁
            //或者只是简单的健壮性判断
            //这个lockValueStr!=null为什么要有这个判断
            if (lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)) {
                //满足条件，时间戳过期，设置一个新的时间戳，并返回原先的值
                String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));

                //原来的值为null locktime太长了 ttl的时间太短，执行的时间太长，在执行的过程中，锁已经失效了。所以有getSetResult==null
                //lockValueStr与getSetResult一样，说明其他进程没有对这里进行改变（但已经超时了），获取到锁，执行逻辑，如果其他进程对这里进行改变，说明其他进程获取到锁，这里不执行业务逻辑
                if (getSetResult == null || getSetResult != null && StringUtils.equals(lockValueStr, getSetResult)) {
                    closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                } else {
                    log.info("没有获得分布式锁", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
                }
            } else {
                log.info("没有获得分布式锁 {}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }
        }

        log.info("关闭订单定时任务结束");
    }

    //@Scheduled(cron = "0 */1 * * * *")
    public void closeOrderTaskV4(){
        log.info("关闭订单定时任务启动");
        RLock lock=redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        boolean getLock=false;
        try {
            if(getLock=lock.tryLock(0,50, TimeUnit.SECONDS)){//第一个时间是等待获得锁的时间，第二个时间是锁多久
                //int hour= Integer.parseInt(PropertiesUtil.getProperty("closeOrder.task.time.hour","2"));
                //iOrderService.closeOrder(hour);
                log.info("Redisson获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            }else{
                log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            log.info("Redisson分布式锁获取异常");
        }finally {
            if(!getLock){
                return;//没获取到锁 没有释放
            }
            lock.unlock();
            log.info("Redisson分布式锁释放");
        }
        log.info("关闭订单定时任务开启");
    }




    private void closeOrder(String lockName) {
        //设置锁的有效期，避免死锁，有效期一般大于业务逻辑执行时间和locktime
        RedisShardedPoolUtil.expire(lockName, 50);
        log.info("获取:{}, ThreadName:{} ", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
        //int hour= Integer.parseInt(PropertiesUtil.getProperty("closeOrder.task.time.hour","2"));
        //iOrderService.closeOrder(hour);
        RedisShardedPoolUtil.del(lockName);
        log.info("释放:{}, ThreadName:{} ", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
    }
}
